#include <iostream>
#include <string>
using namespace std;

//c++泛型编程 模板和STL
//函数模板 类模板

//函数模板的作用
//建立一个通用函数 其函数的返回值类型和形参类型可以不具体指定 用一个虚拟的类型来代表

//函数模板的调用
//1、自动类型推导
//2、显示指定类型 指定模板中的数据类型

template<typename T>
void mySwap1(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

void test10()
{
    int a = 10;
    int b = 20;
    mySwap1(a, b);//自动类型推导
    cout << a << ";" << b << endl;

    float c = 10.0f;
    float d = 20.0f;
    mySwap1<float>(c, d);//显示指定类型 即指定 template<typename T>中 T的类型
    cout << c << ";" << d << endl;

}

//函数模板的注意事项
//1、自动类型推导 必须推导出一致的数据类型 才可以使用
//2、模板必须要确定出T的类型 才可以使用

//数组排序的函数模板案例
//从大到小排序
template<typename T>
void mySort20(T *arr, int len)
{
    for(int i = 0; i < len - 1; i++)//3个元素 只要把前面2个元素排好序就可以
    {
        int max_index = i;
        for(int j = i + 1; j < len; j++)
        {
            if(arr[j] > arr[max_index])
            {
                max_index = j;//更新最大值下标
            }
        }
        if(max_index != i)
        {
            mySwap1(arr[i], arr[max_index]);//上面实现的交换函数模板
        }
    }
}

template<typename T>
void myPrint20(T *arr, int len)
{
    for(int i = 0; i < len; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

void test20()
{
    char ch[] = "achdbc";
    int len = sizeof(ch)/sizeof(ch[0]);
    mySort20(ch, len);//自动推导
    myPrint20(ch, len);

    int arr[] = {2, 5, 6, 3, 9, 20};
    len = sizeof(arr)/sizeof(arr[0]);
    mySort20<int>(arr, len);//显示指定
    myPrint20(arr, len);
}

//普通函数和函数模板的区别
//普通函数调用时可以发生自动类型转换（隐式类型转换）
//函数模板调用时 自动类型推导 不会发生隐式类型转换
//函数模板调用时 如果用显示指定类型的方式 可以发生隐式类型转换

//普通函数和函数模板的调用规则
//如果函数模板和普通函数都可以实现 优先调用普通函数
//可以通过空模板参数列表强制调用函数模板
//函数模板也可以重载
//如果函数模板可以产生更好的匹配 优先调用函数模板

void myPrint30(int a, int b)
{
    cout << "myPrint()" << endl;
}

template<typename T>
void myPrint30(T a, T b)
{
    cout << "template myPrint()" <<endl;
}

//函数模板重载
template<typename T>
void myPrint30(T a, T b, T c)
{
    cout << "template myPrint(a, b, c)" <<endl;
}

void test30()
{
    int a = 10;
    int b = 20;
    myPrint30(a, b);//普通函数
    myPrint30<>(a, b);//函数模板
    myPrint30(a, b, 199);

    char ch1 = 'a';
    char ch2 = 'b';
    myPrint30(ch1, ch2);//普通函数可以类型转换 也可以调用 但是函数模板匹配性更高

}

//模板的局限性 
//模板并不是万能的 有些特定的数据类型 需要具体化方式作特殊实现

class Person40
{
public:
    Person40(string name, int age)
    {
        this->age = age;
        this->name = name;
    }
    int age;
    string name;
};

template<typename T>
bool myCompare40(T &a, T &b)
{
    if(a == b)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//利用具体化Person40对比版本 实现优化后的对比
//利用具体化的模板 可以解决具体类型的通用化
template<> bool myCompare40(Person40 &a, Person40 &b)
{
    if(a.age == b.age && a.name == b.name)
    {
        return true;
    }
    else
    {
        return false;
    }
}


void test40()
{
    Person40 p1("Jack", 29);
    Person40 p2("Tom", 29);
    bool ret = myCompare40(p1, p2);//原始函数模板不能对比Person40类型 需要进行显示专用化优化 template<> bool myCompare40(Person40 &a, Person40 &b)
    if(ret)
    {
        cout << "p1 == p2" << endl;
    }
    else
    {
        cout << "p1 != p2" << endl;
    }
}


//类模板

template<class NameType, class AgeType>
class Person50
{
public:
    Person50(NameType name, AgeType age)
    {
        this->age = age;
        this->name = name;
    }

    NameType name;
    AgeType age;
};

void test50()
{
    Person50<string, int> p1("Jack", 20);//显示指定类型
    cout << p1.name << ";" << p1.age << endl;
}


//类模板和函数模板的区别
//类模板没有自动类型推导的使用方式
//类模板在模板参数列表中可以有默认参数

//类模板中成员函数的创建时机
//普通函数成员函数一开始就创建了
//类模板中成员函数在调用时创建 因为不知道数据类型

//类模板对象作函数参数 针对函数调用时 需要传入类模板的类型时 类模板的处理方式
//类模板实例化的对象 向函数传参的方式
//分3种
//1、指定传入的类型 直接显式指定对象的数据类型
//2、参数模板化 将对象中的参数变为模板进行传递
//3、整个类模板化 将这个对象类型 模板化进行传递

template<class T1, class T2>
class Person60
{
public:
    Person60(T1 name, T2 age)
    {
        this->age = age;
        this->name = name;
    }
    void showPerson()
    {
        cout << name << ";" << age << endl;
    }
    T1 name;
    T2 age;
};

void printPerson60( Person60<string, int> &p) //显示指定类型
{
    p.showPerson();
}

//参数模板化 利用类模板本身参数就是不确定的
template<class T1, class T2>
void printPerson61( Person60<T1, T2> &p)
{
    p.showPerson();
    cout << "T1 type : " << typeid(T1).name() << endl;//打印类型名称
    cout << "T2 type : " << typeid(T2).name() << endl;
}


//整个类模板化
template<class T>
void printPerson62( T &p )
{
    p.showPerson();
    cout << "T type : " << typeid(T).name() << endl;
}

void test60()
{
    Person60<string, int> p1("Jack", 20);
    printPerson60(p1);//显示指定类型

    printPerson61(p1);//参数模板化

    printPerson62(p1);//整个类模板化
}


//类模板与继承
//当类模板碰到继承 需要注意以下几点
//1、当子类继承的父类是一个类模板 子类在声明的时候 要指定出父类中T的类型
//2、如果不指定 编译器无法给子类分配内存
//3、如果想要灵活指定父类中T的类型 子类也需要变成类模板
//总结：如果子类要灵活 子类需要变成类模板 如果不需要灵活 需要指定父类的类型字再继承

template<class T>
class Base70
{
    T m;
};

//1、显示指定父类类型
class Son70 : public Base70<int>
{

};

//2、子类也是类模板 这样父类不需要指定类型
template<class T1, class T2>
class Son71 : public Base70<T1>
{
    T2 a;
};

void test70()
{
    Son70 s1;//这个子类所有的类型都是确定的
    Son71<string, int> s2;//这个子类也是个类模板
}

//类模板成员函数类外实现
template<class T1, class T2>
class Person80
{
public:
    Person80(T1 name, T2 age);//类外实现

    void showPerson();

    T1 name;
    T2 age;
};

//类外实现有参构造
template<class T1, class T2>
Person80<T1, T2>::Person80(T1 name, T2 age)
{
    this->age = age;
    this->name = name;
}

//类外实现成员函数
template<class T1, class T2>
void Person80<T1, T2>::showPerson()
{
    cout << this->age << ";" << this->name << endl;
}

void test80()
{
    Person80<string, int> p("Jack", 20);
    p.showPerson();
}

//类模板分文件编写
//类模板成员函数创建时机是在调用阶段 导致分文件编写时链接不到 即只看到声明时 无法确定函数是什么样子的
//解决方式1 直接包含 类模板成员函数实现的cpp 源文件
//解决方式2 将声明和实现写到同一个文件中 更改后缀名为 hpp 包含hpp文件


//类模板和友元
//全局函数类内实现 直接再类内声明友元即可
//全局函数类外实现 需要提前让编译器知道全局函数的存在

template<class T1, class T2>
class Person90;

//全局函数类外实现
template<class T1, class T2>
void showPerson91(Person90<T1, T2> &p);//这里不知道Peron90这个类 提前声明后续实现 或者直接在这里实现都可以
// {
//     cout << p.name << ";" << p.age << endl;
// }


template<class T1, class T2>
class Person90
{
    //全局函数类内实现
    friend void showPerson90(Person90<T1, T2> &p)
    {
        cout << p.name << ";" << p.age << endl;
    }

    //全局函数类外实现
    //因为不加 <> 表示这是一个普通函数 但是这个函数在类外实现时要加 template 又变成了函数模板 所以报错
    //因此此处要加 <> 让全局函数变成一个函数模板 就可以在类外实现
    //但是变成函数模板后 编译器一开始也不知道这个全局函数张啥样 所以要提前让编译器知道这个模板函数长啥样 因此函数实现要在这之前
    //friend void showPerson91(Person90<T1, T2> &p); //报错
    friend void showPerson91<>(Person90<T1, T2> &p); //正确
public:
    Person90(T1 name, T2 age)
    {
        this->age = age;
        this->name = name;
    }
private:
    T1 name;
    T2 age;
};

template<class T1, class T2>
void showPerson91(Person90<T1, T2> &p)//这里不知道Peron90这个类 提前声明
{
    cout << p.name << ";" << p.age << endl;
}

void test90()
{
    Person90<string, int> p1("Jack", 20);
    showPerson90(p1);//类内实现
    showPerson91(p1);//类外实现
}




int main()
{
    //函数模板
    //test10();
    //函数模板案例
    //test20();

    //普通函数 函数模板调用规则
    //test30();

    //模板的局限性
    //test40();

    //类模板
    //test50();

    //类模板的传参方式
    //test60();

    //类模板的继承
    //test70();

    //类模板成员函数类外实现
    //test80();

    //类模板友元
    test90();

    return 0;
}